package com.internetCafes.spms.common.module.mybatis.plugin;

import com.internetCafes.spms.common.CommonConstants;
import com.internetCafes.spms.common.utils.ShiroUtils;
import com.internetCafes.spms.core.annotation.NoTentant;
import com.internetCafes.spms.core.annotation.TentantFilter;
import com.sms.common.util.hutool.core.util.ObjectUtil;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.shiro.UnavailableSecurityManagerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.Properties;



/**
 * @Intercepts 说明是一个拦截器
 * @Signature 拦截器的签名
 * type 拦截的类型 四大对象之一( Executor,ResultSetHandler,ParameterHandler,StatementHandler)
 * method 拦截的方法
 * args 参数
 */
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class MultiTenancyInterceptor implements Interceptor {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private static final String COUNT_STRING = "_COUNT";

    private static final String CUSTOMER_NAMESPACE = "com.internetCafes.spms.web.customer.mapper"; //新增的Mapper，采用mybatis-plus自带的多租户功能
    private static final String EXTEND_NAMESPACE = "com.internetCafes.spms.web.extend.mapper"; //新增的Mapper，采用mybatis-plus自带的多租户功能
    private static final String PROJECT_NAMESPACE = "com.internetCafes.spms.web.project.mapper"; //新增的Mapper，采用mybatis-plus自带的多租户功能
    private static final String TOOLBOX_NAMESPACE = "com.internetCafes.spms.web.toolbox.mapper"; //新增的Mapper，采用mybatis-plus自带的多租户功能

    @Override
    public Object intercept(Invocation invocation) throws Throwable {

        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        MetaObject metaObjectHandler = SystemMetaObject.forObject(statementHandler);
        // 分离代理对象链
        while (metaObjectHandler.hasGetter("h")) {
            Object obj = metaObjectHandler.getValue("h");
            metaObjectHandler = SystemMetaObject.forObject(obj);
        }
        while (metaObjectHandler.hasGetter("target")) {
            Object obj = metaObjectHandler.getValue("target");
            metaObjectHandler = SystemMetaObject.forObject(obj);
        }

        MappedStatement mappedStatement = (MappedStatement) metaObjectHandler.getValue("delegate.mappedStatement");
        String namespace = mappedStatement.getId();

        if(!mappedStatement.getSqlCommandType().name().equals(SqlCommandType.SELECT.name())){
            return invocation.proceed();
        }

        if(namespace.startsWith(EXTEND_NAMESPACE)
                || namespace.startsWith(PROJECT_NAMESPACE)
                || namespace.startsWith(TOOLBOX_NAMESPACE)
                || namespace.startsWith(CUSTOMER_NAMESPACE)){
            return invocation.proceed();
        }

        String className = namespace.substring(0,namespace.lastIndexOf("."));
        String methodName= namespace.substring(namespace.lastIndexOf(".") + 1,namespace.length());
        Method[] ms = Class.forName(className).getMethods();
        String targetMethodName = getMethodName(methodName); // 为了兼容PageHelper分页插件
        for(Method m : ms){
            if(m.getName().equals(targetMethodName)){
                if(m.getAnnotation(NoTentant.class) != null){
                    // Do nothing.
                } else {
                    Long userId;
                    try {
                        userId = ShiroUtils.getUserId();
                    } catch (UnavailableSecurityManagerException e) {
                        logger.warn(e.getMessage());
                        continue;
                    }
                    if(userId == CommonConstants.SUPER_ADMIN){
                        break;
                    }
                    String tableAlias = null;
                    if (m.getAnnotation(TentantFilter.class) != null){
                        TentantFilter t = m.getAnnotation(TentantFilter.class);
                        tableAlias = t.tableAlias();
                    }

                    BoundSql boundSql = mappedStatement.getBoundSql(invocation.getArgs()[1]);
                    if (SqlParserUtils.isHaveEmptyList(boundSql.getSql())) {
                        return SqlParserUtils.getDefaultReturnValue(invocation);
                    }

                    BoundSql _boundSql = statementHandler.getBoundSql();
                    String sql = SqlParserUtils.parseSQL(_boundSql.getSql(), tableAlias, ShiroUtils.getUserEntity()==null? 0:ShiroUtils.getUserEntity().getDeptId());
                    metaObjectHandler.setValue("delegate.boundSql.sql", sql);
                }
                break;
            }
        }
        return invocation.proceed();
    }

    /**
     * 为了兼容分页插件的COUNT方法
     * @param methodName
     * @return
     */
    private String getMethodName(String methodName){
        String targetMethodName = methodName;
        if(targetMethodName.endsWith(COUNT_STRING)){
            targetMethodName = targetMethodName.substring(0,targetMethodName.length() - COUNT_STRING.length());
        }

        return targetMethodName;
    }


    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
        String dialect = properties.getProperty("dialect");
        logger.info("mybatis intercept dialect:{}", dialect);
    }
}
